package com.yvan.serverless.jdbc;

import com.baomidou.mybatisplus.annotation.DbType;
import com.google.common.collect.Maps;
import com.yvan.serverless.mybatis.MapperWatcher;
import com.yvan.serverless.script.internal.GraalInterop;
import com.yvan.serverless.utils.InteropScriptToJavaUtils;
import com.yvan.serverless.utils.ScriptUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.graalvm.polyglot.proxy.ProxyObject;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class InteropDataSource {
    public final org.clever.hinny.data.jdbc.JdbcDataSource delegate;

    @Autowired
    private SqlSessionFactory sqlSessionFactory;

    @Autowired
    private DaoResultTransformService daoResultTransformService;

    public InteropDataSource(org.clever.hinny.data.jdbc.JdbcDataSource delegate) {
        this.delegate = delegate;
    }

    /**
     * 获取数据库类型
     */
    public DbType getDbType() {
        return delegate.getDbType();
    }

    /**
     * 当前数据源是否关闭
     */
    public boolean isClosed() {
        return delegate.isClosed();
    }

    /**
     * 关闭当前数据源
     */
    public void close() throws Exception {
        delegate.close();
    }

    /**
     * 更新数据库表数据
     *
     * @param tableName 表名称
     * @param fields    更新字段值
     * @param whereMap  更新条件字段(只支持=，and条件)
     */
    public int update(String tableName, Map<String, Object> fields, Map<String, Object> whereMap) {
        fields = InteropScriptToJavaUtils.Instance.convertMap(fields);
        whereMap = InteropScriptToJavaUtils.Instance.convertMap(whereMap);
        return delegate.updateTable(tableName, fields, whereMap);
    }

    /**
     * 更新数据库表数据
     *
     * @param tableName 表名称
     * @param fields    更新字段值
     * @param where     自定义where条件(不用写where关键字)
     */
    public int update(String tableName, Map<String, Object> fields, String where) {
        fields = InteropScriptToJavaUtils.Instance.convertMap(fields);
        return delegate.updateTable(tableName, fields, where);
    }

    /**
     * 数据插入到表
     *
     * @param tableName 表名称
     * @param fields    字段名
     */
    public int insert(String tableName, Map<String, Object> fields) {
        fields = InteropScriptToJavaUtils.Instance.convertMap(fields);
        return delegate.insertTable(tableName, fields).getInsertCount();
    }

    /**
     * 数据插入到表
     *
     * @param tableName 表名称
     * @param fields    字段名
     */
    public int delete(String tableName, Map<String, Object> fields) {
        fields = InteropScriptToJavaUtils.Instance.convertMap(fields);
        return delegate.deleteTable(tableName, fields);
    }


    public Object queryScalar(String sqlId) throws Exception {
        return queryScalar(sqlId, Maps.newLinkedHashMap());
    }

    public Object queryScalar(String sqlId, Map<String, Object> paramMap) throws Exception {
        if (MapperWatcher.getXmlException() != null) {
            throw MapperWatcher.getXmlException();
        }
        Object params = ScriptUtils.toValue(paramMap);
        try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
            Map<String, Object> result = sqlSession.selectOne(sqlId, params);
            for (Object value : result.values()) {
                return value;
            }
            return null;
        }
    }

    public ProxyObject queryMap(String sqlId) throws Exception {
        return queryMap(sqlId, Maps.newLinkedHashMap());
    }

    public ProxyObject queryMap(String sqlId, Map<String, Object> paramMap) throws Exception {
        if (MapperWatcher.getXmlException() != null) {
            throw MapperWatcher.getXmlException();
        }
        Object params = ScriptUtils.toValue(paramMap);
        try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
            Map<String, Object> result = sqlSession.selectOne(sqlId, params);
            daoResultTransformService.convert(result);
            return GraalInterop.Instance.fromJMap(result);
        }
    }

    public List<ProxyObject> queryList(String sqlId) throws Exception {
        return queryList(sqlId, Maps.newLinkedHashMap());
    }

    public List<ProxyObject> queryList(String sqlId, Map<String, Object> paramMap) throws Exception {
        if (MapperWatcher.getXmlException() != null) {
            throw MapperWatcher.getXmlException();
        }
        Object params = ScriptUtils.toValue(paramMap);
        try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
            List<Map<String, Object>> result = sqlSession.selectList(sqlId, params);
            return convert(result);
        }
    }

    public int execute(String sqlId) throws Exception {
        return execute(sqlId, Maps.newLinkedHashMap());
    }

    public int execute(String sqlId, Map<String, Object> paramMap) throws Exception {
        if (MapperWatcher.getXmlException() != null) {
            throw MapperWatcher.getXmlException();
        }
        Object params = ScriptUtils.toValue(paramMap);
        try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
            return sqlSession.update(sqlId, params);
        }
    }

    private List<ProxyObject> convert(List<Map<String, Object>> result) {
        List<ProxyObject> ret = new ArrayList<>(result.size());

        for (Map<String, Object> row : result) {
            daoResultTransformService.convert(row);
            ret.add(GraalInterop.Instance.fromJMap(row));
        }

        return ret;
    }
}
